﻿using System;
using System.Linq.Expressions;

namespace BrightSword.Feber.Core
{
    /// <summary>
    ///   Extend this class to build a method of arity 1 (of type <typeparamref name="TInstance"/>), which has an operation for each property of <typeparamref name="TProto"/>.
    /// </summary>
    /// <typeparam name="TProto">The (class or interface) type of the object whose properties are to be enumerated</typeparam>
    /// <typeparam name="TInstance"> The (interface) type of the object being operated upon.</typeparam>
    /// Typically, <typeparamref name="TProto"/> and <typeparamref name="TInstance" /> are isomorphic types, if not the same type 
    /// 
    /// <example>
    ///   Object Printing: For code which prints out an object's properties, we might want to write code as follows
    ///  
    ///  <code>
    ///  void Print(Foo instance)
    ///  {
    ///      Console.WriteLine("{0} (instance)", typeof (T).Name);
    ///      Console.WriteLine("{");
    ///      Console.WriteLine("\tHeight : {0}", instance.Height);
    ///      Console.WriteLine("\tWidth : {0}", instance.Width);
    ///      ...
    ///      Console.WriteLine("}");
    ///  }
    ///  </code>
    /// 
    ///  This is obviously onerous, because one has to write out all the properties by hand.
    ///  Furthermore, it requires maintenance when the list of properties defined on Foo changes.
    ///  Instead, we could use this builder to generate us exactly such a function as follows: 
    /// 
    ///  <code>
    /// static class Printer&lt;TProto&gt;
    /// {
    ///     class PrinterActionBuilder : ActionBuilder&lt;TProto, TProto&gt;
    ///     {
    ///         protected override Expression PropertyExpression(PropertyInfo property, ParameterExpression instanceParameterExpression)
    ///         {
    ///             return Expression.Call(
    ///                 typeof (Console),
    ///                 "WriteLine",
    ///                 null,
    ///                 Expression.Constant("\t{0} : {1}", typeof (string)),
    ///                 Expression.Constant(property.Name, typeof (string)),
    ///                 Expression.Call(typeof (Convert), "ToString", null, Expression.Property(instanceParameterExpression, property)));
    ///         }
    ///     }
    /// 
    ///     private static readonly PrinterActionBuilder _builder = new PrinterActionBuilder();
    /// 
    ///     public static void Print(TProto instance) 
    ///     {
    ///          Console.WriteLine("{0} (instance)", typeof (T).Name);
    ///          Console.WriteLine("{");
    ///          
    ///         _builder.Action(instance);
    /// 
    ///          Console.WriteLine("}");
    ///     }
    /// }
    /// 
    ///  </code>
    ///  This dynamically generates the operation lambdas just once at the static initializer time, 
    ///  and allows the compiler to memoize the functions based on the generic type argument.
    ///  Consequently there is very little loss of runtime performance when Print() is called many times repetitively.
    /// </example>
    public abstract class ActionBuilder<TProto, TInstance> : UnaryOperationBuilderBase<TProto, TInstance>
    {
        private Action<TInstance> _action;

        public virtual Action<TInstance> Action
        {
            get { return _action ?? (_action = BuildAction()); }
        }

        private Action<TInstance> BuildAction()
        {
            // create a block expression surrounding all these expressions
            var blockExpression = Expression.Block(OperationExpressions);

            // create a lambda expression, wiring up the _instance parameter
            var lambdaExpression = Expression.Lambda<Action<TInstance>>(blockExpression,
                                                                        InstanceParameterExpression);

            // voila!
            return lambdaExpression.Compile();
        }
    }

    /// <summary>
    ///   Extend this class to build a method of arity 2 (of types <typeparamref name="TLeftInstance"/> and <typeparamref name="TRightInstance"/>), which has an operation for each property of <typeparamref name="TProto"/>.
    /// </summary>
    /// <typeparam name="TProto">The (class or interface) type of the object whose properties are to be enumerated</typeparam>
    /// <typeparam name="TLeftInstance"> The (interface) type of the object on the left side of some operation.</typeparam>
    /// <typeparam name="TRightInstance"> The (interface) type of the object on the right side of some operation.</typeparam>
    /// Typically, <typeparamref name="TProto"/>, <typeparamref name="TLeftInstance"/> and <typeparamref name="TRightInstance" /> are isomorphic types, if not the same type 
    /// 
    ///<example>
    ///  Assignment: For code which copies an instance of a given type into another instance one property at a time, we might want to write code as follows:
    /// <code>
    /// void Copy(Foo left, IFoo right)
    /// {
    ///     left.Height = right.Height;
    ///     left.Width = right.Width;
    ///     ...
    /// }
    /// </code>
    /// 
    ///  This is obviously onerous, because one has to write out all the properties by hand.
    ///  Furthermore, it requires maintenance when the list of properties defined on Foo changes.
    ///  Instead, we could use this builder to generate us exactly such a function as follows: 
    /// 
    ///  <code>
    /// static class Copier&lt;TProto&gt;
    /// {
    ///     class CopyActionBuilder : ActionBuilder&lt;TProto, TProto, TProto&gt;
    ///     {
    ///         protected override Expression PropertyExpression(PropertyInfo property, 
    ///                                                          ParameterExpression leftInstanceParameterExpression, 
    ///                                                          ParameterExpression rightInstanceParameterExpression)
    ///         {
    ///             return Expression.Assign(Expression.Property(leftInstanceParameterExpression, property), 
    ///                                      Expression.Property(rightInstanceParameterExpression, property));
    ///         }
    ///     }
    /// 
    ///     private static readonly CopyActionBuilder _builder = new CopyActionBuilder();
    /// 
    ///     public static void Copy(TProto left, TProto right) 
    ///     {
    ///         _builder.Action(left, right);
    ///     }
    /// }
    /// 
    ///  </code>
    ///  This dynamically generates the operation lambdas just once at the static initializer time, 
    ///  and allows the compiler to memoize the functions based on the generic type argument.
    ///  Consequently there is very little loss of runtime performance when Copy() is called many times repetitively.
    /// </example>
    public abstract class ActionBuilder<TProto, TLeftInstance, TRightInstance> : BinaryOperationBuilderBase<TProto, TLeftInstance, TRightInstance>
    {
        private Action<TLeftInstance, TRightInstance> _action;

        public virtual Action<TLeftInstance, TRightInstance> Action
        {
            get { return _action ?? (_action = BuildAction()); }
        }

        protected virtual Action<TLeftInstance, TRightInstance> BuildAction()
        {
            // create a block expression surrounding all these expressions
            var blockExpression = Expression.Block(OperationExpressions);

            // create a lambda expression, wiring up the _left and _right parameters
            var lambdaExpression = Expression.Lambda<Action<TLeftInstance, TRightInstance>>(blockExpression,
                                                                                            LeftInstanceParameterExpression,
                                                                                            RightInstanceParameterExpression);

            // voila!
            return lambdaExpression.Compile();
        }
    }
}